-- 
-- Please see the readme.txt file included with this distribution for 
-- attribution and copyright information.
--

function getPowerType(sourcenode)
	-- Default power type is Situational
	local targettype = "a0-situational";
	
	-- Look up the power source and recharge value
	local powersource = string.lower(NodeManager.get(sourcenode, "source", ""));
	local powerrecharge = string.lower(NodeManager.get(sourcenode, "recharge", ""));

	-- Determine the power type
	if string.match(powersource, "cantrip") then
		targettype = "a2-cantrip";
	elseif string.match(powersource, "utility") then
		targettype = "d-utility";
	elseif string.match(powersource, "item") then
		if string.match(powerrecharge, "daily") then
			targettype = "e-itemdaily";
		elseif string.match(powerrecharge, "encounter") then
			targettype = "b1-encounter";
		elseif string.match(powerrecharge, "consumable") then
			targettype = "a11-atwillspecial";
		elseif string.match(powerrecharge, "healing surge") then
			targettype = "a1-atwill";
		elseif string.match(powerrecharge, "/day", 1, true) then
			targettype = "a11-atwillspecial";
		else
			targettype = "a1-atwill";
		end
	elseif string.match(powerrecharge, "at%-will") then
		if string.match(powerrecharge, "special") then
			targettype = "a11-atwillspecial";
		else
			targettype = "a1-atwill";
		end
	elseif string.match(powerrecharge, "encounter") then
		if string.match(powerrecharge, "special") then
			targettype = "b11-encounterspecial";
		else
			local powername = string.lower(NodeManager.get(sourcenode, "name", ""));
			if string.match(powername, "channel divinity: ") then
				targettype = "b2-channeldivinity";
			else
				targettype = "b1-encounter";
			end
		end
	elseif string.match(powerrecharge, "daily") then
		targettype = "c-daily";
	end

	-- Return the power type
	return targettype;
end

function addPowerDB(charnode, sourcenode, sNodeClass, powertype)
	-- Validate parameters
	if not charnode or not sourcenode then
		return;
	end

	-- Make sure we have a powertype
	if not powertype then
		powertype = CharSheetCommon.getPowerType(sourcenode);
	end

	-- Get the powers node
	local powersnode = NodeManager.createChild(charnode, "powers");
	local powertypenode = NodeManager.createChild(powersnode, powertype);
	local powerlistnode = NodeManager.createChild(powertypenode, "power");
	local newpower = NodeManager.createChild(powerlistnode);
							
	-- Change the power mode so that all powers are shown
	NodeManager.set(charnode, "powermode", "string", "standard");

	-- Make sure the type node is visible on the character sheet, now that we're adding a power to it
	if powertype == "a11-atwillspecial" then
		NodeManager.set(charnode.getChild("powershow"), "atwillspecial", "number", 1);
	elseif powertype == "a2-cantrip" then
		NodeManager.set(charnode.getChild("powershow"), "cantrip", "number", 1);
	elseif powertype == "b11-encounterspecial" then
		NodeManager.set(charnode.getChild("powershow"), "encounterspecial", "number", 1);
	elseif powertype == "b2-channeldivinity" then
		NodeManager.set(charnode.getChild("powershow"), "channeldivinity", "number", 1);
	end
	
	-- Set up the basic power fields
  	local name = NodeManager.get(sourcenode, "name", "");
  	local source = NodeManager.get(sourcenode, "source", "");
  	if source == "" then
  		local nodeOverParent = sourcenode.getChild("....");
  		if nodeOverParent then
  			if nodeOverParent.getName() == "item" or nodeOverParent.getName() == "inventorylist" then
  				source = "Item";
  			end
  		end
  	end
  	if source == "Item" then
  		if string.match(name, "^Power -") then
  			local nodeItemName = sourcenode.getChild("...name");
  			if nodeItemName then
  				name = string.gsub(name, "Power -", nodeItemName.getValue(), 1);
  			end
  		else
  			name = string.gsub(name, " Power - ", " ");
  		end
  	end
  	NodeManager.set(newpower, "name", "string", name);
  	NodeManager.set(newpower, "source", "string", source);
  	NodeManager.set(newpower, "recharge", "string", NodeManager.get(sourcenode, "recharge", "-"));
  	NodeManager.set(newpower, "keywords", "string", NodeManager.get(sourcenode, "keywords", "-"));
  	NodeManager.set(newpower, "range", "string", NodeManager.get(sourcenode, "range", "-"));
	NodeManager.set(newpower, "shortdescription", "string", NodeManager.get(sourcenode, "shortdescription", ""));

	-- Set up the action field
	local poweraction = NodeManager.get(sourcenode, "action", "-");
	if poweraction == "Standard Action" then
		poweraction = "Standard";
	elseif poweraction == "Move Action" then
		poweraction = "Move";
	elseif poweraction == "Minor Action" then
		poweraction = "Minor";
	elseif poweraction == "Free Action" then
		poweraction = "Free";
	elseif poweraction == "Immediate Interrupt" then
		poweraction = "Interrupt";
	elseif poweraction == "Immediate Reaction" then
		poweraction = "Reaction";
	end
	NodeManager.set(newpower, "action", "string", poweraction);

	-- Set the shortcut
	local refnode = NodeManager.createChild(newpower, "shortcut", "windowreference");
	refnode.setValue(sNodeClass, sourcenode.getNodeName());
	
	-- Finally, parse the description
	parseDescription(newpower);
	
	-- Return the new power created
	return newpower, powertype;
end

function parseDescription(powernode)
	-- Clear the attack and effect entries
	local powerabilitynode = NodeManager.createChild(powernode, "abilities");
	for k,v in pairs(powerabilitynode.getChildren()) do
		v.delete();
	end
	
	-- Pick up the power abilities from the description
	local powerdesc = NodeManager.get(powernode, "shortdescription", "");
	local powerabilitylist = PowersManager.parsePowerDescription(powernode, "");
	
	-- Helper variables for building ability items
	local effects_created = {};
	local ability_count = 0;
	local prev_attack_entries = {};

	-- Iterate through abilities to add to power ability list
	for i = 1, #powerabilitylist do
	
		-- See if we should create a new ability entry
		-- If damage ability, then use the last entry if following an attack ability
		-- If effect ability, then make sure it's unique
		local ability_entry = nil;
		local createflag = true;
		if powerabilitylist[i].type == "effect" then
			local effect_key = powerabilitylist[i].name .. powerabilitylist[i].expire .. powerabilitylist[i].mod;
			if effects_created[effect_key] then
				createflag = false;
			else
				effects_created[effect_key] = true;
			end
		elseif powerabilitylist[i].type == "damage" and #prev_attack_entries > 0 then
			createflag = false;
			ability_entry = prev_attack_entries[1];
			table.remove(prev_attack_entries, 1);
		end

		-- Create a new ability entry, if needed
		if createflag then
			ability_entry = NodeManager.createChild(powerabilitynode);

			if powerabilitylist[i].type == "attack" then
				table.insert(prev_attack_entries, ability_entry);
			else
				prev_attack_entries = {};
			end

			if powerabilitylist[i].type == "damage" then
				NodeManager.set(ability_entry, "type", "string", "attack");
			else
				NodeManager.set(ability_entry, "type", "string", powerabilitylist[i].type);
			end

			ability_count = ability_count + 1;
			NodeManager.set(ability_entry, "order", "number", ability_count);
		end

		-- Fill in the ability entry details
		if ability_entry then
			if powerabilitylist[i].type == "attack" then
				if #(powerabilitylist[i].clauses) > 0 then
					if #(powerabilitylist[i].clauses[1].stat) > 0 then
						NodeManager.set(ability_entry, "attackstat", "string", powerabilitylist[i].clauses[1].stat[1]);
					end
					NodeManager.set(ability_entry, "attackstatmodifier", "number", powerabilitylist[i].clauses[1].mod);
				end
				NodeManager.set(ability_entry, "attackdef", "string", powerabilitylist[i].defense);
			elseif powerabilitylist[i].type == "damage" then
				if #(powerabilitylist[i].clauses) > 0 then
					if #(powerabilitylist[i].clauses[1].stat) > 0 then
						local tempstr = powerabilitylist[i].clauses[1].stat[1];
						if string.match(tempstr, "^half") then
							NodeManager.set(ability_entry, "damagestatmult", "string", "half");
							tempstr = string.sub(tempstr, 5);
						end
						if string.match(tempstr, "^double") then
							NodeManager.set(ability_entry, "damagestatmult", "string", "double");
							tempstr = string.sub(tempstr, 7);
						end
						NodeManager.set(ability_entry, "damagestat", "string", tempstr);
					end
					if #(powerabilitylist[i].clauses[1].stat) > 1 then
						local tempstr = powerabilitylist[i].clauses[1].stat[2];
						if string.match(tempstr, "^half") then
							NodeManager.set(ability_entry, "damagestatmult2", "string", "half");
							tempstr = string.sub(tempstr, 5);
						end
						if string.match(tempstr, "^double") then
							NodeManager.set(ability_entry, "damagestatmult2", "string", "double");
							tempstr = string.sub(tempstr, 7);
						end
						NodeManager.set(ability_entry, "damagestat2", "string", tempstr);
					end

					NodeManager.set(ability_entry, "damagetype", "string", powerabilitylist[i].clauses[1].subtype);

					if powerabilitylist[i].clauses[1].dicestr then
						local dice, modifier = StringManager.convertStringToDice(powerabilitylist[i].clauses[1].dicestr);
						NodeManager.set(ability_entry, "damagebasicdice", "dice", dice);
						NodeManager.set(ability_entry, "damagestatmodifier", "number", modifier);
					end
					if powerabilitylist[i].clauses[1].basemult then
						NodeManager.set(ability_entry, "damageweaponmult", "number", powerabilitylist[i].clauses[1].basemult);
					end
				end
			elseif powerabilitylist[i].type == "heal" then
				if #(powerabilitylist[i].clauses) > 0 then
					if #(powerabilitylist[i].clauses[1].stat) > 0 then
						local tempstr = powerabilitylist[i].clauses[1].stat[1];
						if string.match(tempstr, "^half") then
							NodeManager.set(ability_entry, "healstatmult", "string", "half");
							tempstr = string.sub(tempstr, 5);
						end
						if string.match(tempstr, "^double") then
							NodeManager.set(ability_entry, "healstatmult", "string", "double");
							tempstr = string.sub(tempstr, 7);
						end
						NodeManager.set(ability_entry, "healstat", "string", tempstr);
					end
					if #(powerabilitylist[i].clauses[1].stat) > 1 then
						local tempstr = powerabilitylist[i].clauses[1].stat[2];
						if string.match(tempstr, "^half") then
							NodeManager.set(ability_entry, "healstatmult2", "string", "half");
							tempstr = string.sub(tempstr, 5);
						end
						if string.match(tempstr, "^double") then
							NodeManager.set(ability_entry, "healstatmult2", "string", "double");
							tempstr = string.sub(tempstr, 7);
						end
						NodeManager.set(ability_entry, "healstat2", "string", tempstr);
					end

					if powerabilitylist[i].clauses[1].dicestr then
						local dice, modifier = StringManager.convertStringToDice(powerabilitylist[i].clauses[1].dicestr);
						NodeManager.set(ability_entry, "healdice", "dice", dice);
						NodeManager.set(ability_entry, "healmod", "number", modifier);
					end

					if powerabilitylist[i].clauses[1].subtype then
						NodeManager.set(ability_entry, "healtype", "string", powerabilitylist[i].clauses[1].subtype);
					end

					if powerabilitylist[i].clauses[1].basemult then
						NodeManager.set(ability_entry, "hsvmult", "number", powerabilitylist[i].clauses[1].basemult);
					end
					if powerabilitylist[i].clauses[1].cost then
						NodeManager.set(ability_entry, "healcost", "number", powerabilitylist[i].clauses[1].cost);
					end
				end
			elseif powerabilitylist[i].type == "effect" then
				NodeManager.set(ability_entry, "label", "string", powerabilitylist[i].name);
				NodeManager.set(ability_entry, "expiration", "string", powerabilitylist[i].expire);
				NodeManager.set(ability_entry, "effectsavemod", "number", powerabilitylist[i].mod);
				NodeManager.set(ability_entry, "apply", "string", powerabilitylist[i].sApply);
				NodeManager.set(ability_entry, "targeting", "string", powerabilitylist[i].sTargeting);
			end  -- END ABILITY TYPE SWITCH
		end -- END ABILITY WINDOW EXISTENCE CHECK
	end  -- END POWER ABILITY LIST LOOP
end

function simplifyPowerDescription(powernode)
	-- Get the current power description
	local powerdesc = NodeManager.get(powernode, "shortdescription", "");

	-- Now, simplify the description
	local sFinal = "";
	local clauses, clauses_stats = StringManager.split(powerdesc, ";", true);
	for i = 1, #clauses do
		local sClauseLabel = string.match(clauses[i], "^([^:]+):");
		local sClauseRemainder = "";
		if sClauseLabel then
			sClauseRemainder = StringManager.trimString(string.sub(clauses[i], #sClauseLabel + 2));
		else
			sClauseLabel = "";
			sClauseRemainder = clauses[i];
		end
		
		sAdd = clauses[i];
		
		if sClauseLabel == "Target" and sClauseRemainder == "One creature" then
			sAdd = "";
		end
		
		local clause_abilities = {};
		for k,v in pairs(powerabilitylist) do
			if v.startpos >= clauses_stats[i].startpos and v.startpos < clauses_stats[i].endpos then
				table.insert(clause_abilities, v);
			end
		end

		if #clause_abilities == 1 and clause_abilities[1].type == "attack" then
			if (string.sub(sClauseLabel, -6) == "Attack") and (clause_abilities[1].endpos >= clauses_stats[i].endpos) then
				sAdd = "";
			end
		elseif #clause_abilities >= 1 and clause_abilities[1].type == "damage" then
			if (sClauseLabel == "Hit") and (clause_abilities[1].startpos <= clauses_stats[i].startpos + 5) then
				local sRemainder = string.sub(powerdesc, clause_abilities[1].endpos, clauses_stats[i].endpos - 1);
				local sSep = string.sub(sRemainder, 1, 1);
				if sSep == "," or sSep == "." then
					sRemainder = string.sub(sRemainder, 3);
					if string.sub(sRemainder, 1, 4) == "and " then
						sRemainder = string.upper(string.sub(sRemainder, 5, 5)) .. string.sub(sRemainder, 6);
					end
					while string.sub(sRemainder, 1, 1) == "\r" do	
						sRemainder = string.sub(sRemainder, 2);
					end
		
					if sRemainder == "" then
						sAdd = "";
					else
						sAdd = sClauseLabel .. ": " .. sRemainder;
					end
				elseif sSep == "" then
					sAdd = "";
				end
			end
		end
		
		if sAdd ~= "" then
			if sFinal ~= "" then
				sFinal = sFinal .. "; ";
			end
			sFinal = sFinal .. sAdd;
		end
	end
	
	-- Space savers
	sFinal = string.gsub(sFinal, "vs. Fortitude", "vs. Fort");
	sFinal = string.gsub(sFinal, "vs. Reflex", "vs. Ref");
	sFinal = string.gsub(sFinal, "Strength vs.", "STR vs.");
	sFinal = string.gsub(sFinal, "Constitution vs.", "CON vs.");
	sFinal = string.gsub(sFinal, "Dexterity vs.", "DEX vs.");
	sFinal = string.gsub(sFinal, "Intelligence vs.", "INT vs.");
	sFinal = string.gsub(sFinal, "Wisdom vs.", "WIS vs.");
	sFinal = string.gsub(sFinal, "Charisma vs.", "CHA vs.");
	sFinal = string.gsub(sFinal, "Strength modifier", "STR");
	sFinal = string.gsub(sFinal, "Constitution modifier", "CON");
	sFinal = string.gsub(sFinal, "Dexterity modifier", "DEX");
	sFinal = string.gsub(sFinal, "Intelligence modifier", "INT");
	sFinal = string.gsub(sFinal, "Wisdom modifier", "WIS");
	sFinal = string.gsub(sFinal, "Charisma modifier", "CHA");

	-- Finally, set the description string to its new value
	if sFinal == "" then
		sFinal = "-";
	end
	NodeManager.set(powernode, "shortdescription", "string", sFinal);
end

function getDefaultFocus(charnode, focustype)
	-- Make sure we have a correct parameter
	local tool_order = 0;
	if focustype == "weapon" then
		tool_order = NodeManager.get(charnode, "powerfocus.weapon.order", 0);
	elseif focustype == "implement" then
		tool_order = NodeManager.get(charnode, "powerfocus.implement.order", 0);
	else
		return nil;
	end
	
	-- Look up the weapon node to make sure it is valid
	local weapon_node = nil;
	if tool_order > 0 then
		local weapons_node = charnode.getChild("weaponlist");
		if weapons_node then
			for k, v in pairs(weapons_node.getChildren()) do
				local order = NodeManager.get(v, "order", 0);
				if order == tool_order then
					weapon_node = v;
				end
			end
		end
	end
	
	-- Return the weapon node found
	return weapon_node;
end

function addPowerToWeaponDB(charnode, sourcenode)
	-- Parameter validation
	if not charnode or not sourcenode then
		return nil;
	end
	
	-- Create the new weapon entry
	local weaponlist = NodeManager.createChild(charnode, "weaponlist");
	if not weaponlist then
		return nil;
	end
	local weaponnode = NodeManager.createChild(weaponlist);
	if not weaponnode then
		return nil;
	end
	
	-- Determine the weapon number
	local order = calcNextWeaponOrder(weaponlist);
	NodeManager.set(weaponnode, "order", "number", order);

	-- Fill in the basic attributes
	NodeManager.set(weaponnode, "name", "string", NodeManager.get(sourcenode, "name", ""));
	
	-- Determine the attack type and range increment
	local range = NodeManager.get(sourcenode, "range", "");
	local rrangeval = string.match(range, "Ranged (%d+)");
	local brangeval = string.match(range, "Area burst %d+ within (%d+)");
	local wrangeval = string.match(range, "Area wall %d+ within (%d+)");
	if rrangeval then
		NodeManager.set(weaponnode, "type", "number", 1);
		NodeManager.set(weaponnode, "rangeincrement", "number", tonumber(rrangeval) or 0);
	elseif brangeval then
		NodeManager.set(weaponnode, "type", "number", 1);
		NodeManager.set(weaponnode, "rangeincrement", "number", tonumber(brangeval) or 0);
	elseif wrangeval then
		NodeManager.set(weaponnode, "type", "number", 1);
		NodeManager.set(weaponnode, "rangeincrement", "number", tonumber(wrangeval) or 0);
	end

	-- Load up the properties field
	local propstr = "";

	local actionval = NodeManager.get(sourcenode, "action", "");
	if actionval == "Standard Action" then
		propstr = propstr .. "[s]";
	elseif actionval == "Move Action" then
		propstr = propstr .. "[mo]";
	elseif actionval == "Minor Action" then
		propstr = propstr .. "[mi]";
	elseif actionval == "Free Action" then
		propstr = propstr .. "[f]";
	elseif actionval == "Immediate Interrupt" then
		propstr = propstr .. "[i]";
	elseif actionval == "Immediate Reaction" then
		propstr = propstr .. "[r]";
	end
	
	local rechargeval = NodeManager.get(sourcenode, "recharge", "");
	if string.sub(rechargeval, 1, 9) == "Encounter" then
		propstr = propstr .. "[e]";
	elseif string.sub(rechargeval, 1, 5) == "Daily" then
		propstr = propstr .. "[d]";
	end

	local keywords = NodeManager.get(sourcenode, "keywords", "");
	if keywords ~= "" then
		if propstr ~= "" then
			propstr = propstr .. ", ";
		end
		propstr = propstr .. keywords;
	end

	NodeManager.set(weaponnode, "properties", "string", propstr);

	-- Determine if this is a weapon/implement power
	local tool_node = nil;
	if string.match(string.lower(keywords), "weapon") then
		tool_node = getDefaultFocus(charnode, "weapon");
	elseif string.match(string.lower(keywords), "implement") then
		tool_node = getDefaultFocus(charnode, "implement");
	end

	-- Finally, parse the description string for attack and damage clauses
	local shortdesc = NodeManager.get(sourcenode, "shortdescription", "");
	local abilities = PowersManager.parsePowerDescription(sourcenode, "");
	
	local attackfound = false;
	local damagefound = false;
	for i = 1, #abilities do
		if not attackfound and abilities[i].type == "attack" and #(abilities[i].clauses) > 0 then
			attackfound = true;

			NodeManager.set(weaponnode, "attackdef", "string", abilities[i].defense);

			if #(abilities[i].clauses[1].stat) > 0 then
				NodeManager.set(weaponnode, "attackstat", "string", abilities[i].clauses[1].stat[1]);
			end
			local mod = abilities[i].clauses[1].mod;
			if tool_node then
				mod = mod + NodeManager.get(tool_node, "bonus", 0);
			end
			NodeManager.set(weaponnode, "bonus", "number", mod);
		end
		if not damagefound and abilities[i].type == "damage" and #(abilities[i].clauses) > 0 then
			damagefound = true;
			
			if abilities[i].clauses[1].dicestr then
				local dice, mod = StringManager.convertStringToDice(abilities[i].clauses[1].dicestr);

				if abilities[i].clauses[1].basemult > 0 and tool_node then
					local basedice = NodeManager.get(tool_node, "damagedice", {});

					for i = 1, abilities[i].clauses[1].basemult do
						for j = 1, #basedice do
							table.insert(dice, basedice[j]);
						end
					end
				end

				if tool_node then
					mod = mod + NodeManager.get(tool_node, "damagebonus", 0);

					local critdice = NodeManager.get(tool_node, "criticaldice", {});
					local critmod = NodeManager.get(tool_node, "criticalbonus", 0)
					local critdmgtype = NodeManager.get(tool_node, "criticaldamagetype", "");

					NodeManager.set(weaponnode, "criticaldice", "dice", critdice);
					NodeManager.set(weaponnode, "criticalbonus", "number", critmod);
					NodeManager.set(weaponnode, "criticaldamagetype", "string", critdmgtype);
				end

				NodeManager.set(weaponnode, "damagedice", "dice", dice);
				NodeManager.set(weaponnode, "damagebonus", "number", mod);
			end

			if #(abilities[i].clauses[1].stat) > 0 then
				NodeManager.set(weaponnode, "damagestat", "string", abilities[i].clauses[1].stat[1]);
			end
			NodeManager.set(weaponnode, "damagetype", "string", abilities[i].clauses[1].subtype);
		end
	end
	
	-- Set the shortcut fields
	local refnode = NodeManager.createChild(weaponnode, "shortcut", "windowreference");
	refnode.setValue("powerdesc", sourcenode.getNodeName());
	
	-- Return the new weapon node
	return weaponnode;
end

function checkForSecondWind(charnode)
	-- Validate parameters
	if not charnode then
		return nil;
	end
	
	-- Get the powers node
	local powersnode = NodeManager.createChild(charnode, "powers");
	local powertypenode = NodeManager.createChild(powersnode, "b1-encounter");
	local powerlistnode = NodeManager.createChild(powertypenode, "power");
	
	-- Check for an existing Second Wind power
	for k,v in pairs(powerlistnode.getChildren()) do
		if NodeManager.get(v, "name", "") == "Second Wind" then
			return v;
		end
	end
	
	-- Create a new power node
	local newpower = NodeManager.createChild(powerlistnode);
	
	-- Set up the basic power fields
  	NodeManager.set(newpower, "name", "string", "Second Wind");
  	NodeManager.set(newpower, "source", "string", "General");
  	NodeManager.set(newpower, "recharge", "string", "Encounter");
  	NodeManager.set(newpower, "keywords", "string", "Healing");
  	NodeManager.set(newpower, "range", "string", "Personal");
	NodeManager.set(newpower, "action", "string", "Standard");
	NodeManager.set(newpower, "shortdescription", "string", "Effect: You spend a healing surge, and gain a +2 bonus to all defenses until the start of your next turn.");
	
	-- Now parse the description to pre-fill the ability items
	parseDescription(newpower);

	-- Return the new power created
	return newpower;
end

function addItemDB(charnode, sourcenode, nodetype)
	-- Validate parameters
	if not charnode or not sourcenode then
		return nil;
	end

	-- Create the new inventory entry
	local itemlist = NodeManager.createChild(charnode, "inventorylist");
	local itemnode = nil;
	local isCustom = false;
	if itemlist then
		-- Basic item
		if nodetype == "referencearmor" or nodetype == "referenceweapon" or nodetype == "referenceequipment" then
			-- Create the child node
			itemnode = NodeManager.createChild(itemlist);
	
			-- Just copy over the fields we need
			NodeManager.set(itemnode, "name", "string", NodeManager.get(sourcenode, "name", ""));
			NodeManager.set(itemnode, "weight", "number", NodeManager.get(sourcenode, "weight", 0));
			
			-- Set the shortcut fields
			local refnode = NodeManager.createChild(itemnode, "shortcut", "windowreference");
			refnode.setValue(nodetype, sourcenode.getNodeName());

		-- Custom or magic item
		elseif nodetype == "item" or nodetype == "referencemagicitem" then
			-- Copy over all the item fields, since we are making a personal copy of the item
			itemnode = NodeManager.copy(sourcenode, itemlist, true);
			
			-- Flag that we made a personal custom item
			isCustom = true;
			
			-- Set the initial item type
			local itemclass = NodeManager.get(itemnode, "class", "");
			local itemtype = "other";
			if itemclass == "Armor" then
				itemtype = "armor";
			elseif itemclass == "Weapon" or itemclass == "Implement" then
				itemtype = "weapon";
			end
			NodeManager.set(itemnode, "mitype", "string", itemtype);
			
			-- Set the shortcut fields
			local refnode = NodeManager.createChild(itemnode, "shortcut", "windowreference");
			refnode.setValue("item", itemnode.getNodeName());
		end
	end
	
	-- Create the new weapon entry, if appropriate
	local isWeapon = false;
	local weaponnode1 = nil;
	local weaponnode2 = nil;
	if nodetype == "referenceweapon" then
		isWeapon = true;
	elseif nodetype == "referenceequipment" then
		if NodeManager.get(sourcenode, "type", "") == "Implement" then
			isWeapon = true;
		end
	elseif nodetype == "item" or nodetype == "referencemagicitem" then
		local itemclass = NodeManager.get(sourcenode, "class", "");
		if itemclass == "Weapon" or itemclass == "Implement" then
			isWeapon = true;
		end
	end
	if isWeapon then
		if isCustom then
			weaponnode1, weaponnode2 = addWeaponDB(charnode, sourcenode, itemnode, "item");
		else
			weaponnode1, weaponnode2 = addWeaponDB(charnode, sourcenode, sourcenode, nodetype);
		end
	end
	
	return itemnode, weaponnode1, weaponnode2;
end

function removeWeaponDB(charnode, itemnodename)
	-- Parameter validation
	if not charnode or not itemnodename then
		return false;
	end
	
	-- Get the weapon list we are going to remove from
	local weaponlist = NodeManager.createChild(charnode, "weaponlist");
	if not weaponlist then
		return false;
	end

	-- Check to see if any of the weapon nodes linked to this item node should be deleted
	local foundmelee = false;
	local foundranged = false;
	for k, v in pairs(weaponlist.getChildren()) do
		local scnode = v.getChild("shortcut");
		if scnode then
			local refclass, refnode = scnode.getValue();
			if refnode == itemnodename then
				local typeval = NodeManager.get(v, "type", 0);
				if typeval == 1 and not foundmelee then
					foundmelee = true;
					v.delete();
				elseif typeval == 0 and not foundranged then
					foundranged = true;
					v.delete();
				end
			end
		end
	end
	
	-- We didn't find any linked weapons
	return (foundmelee or foundranged);
end

function calcNextWeaponOrder(weaponlist)
	local order = 1;
	
	if weaponlist then
		local ordertable = {};
		for k,v in pairs(weaponlist.getChildren()) do
			local temp_order = NodeManager.get(v, "order", 0);
			ordertable[temp_order] = true;
		end

		while ordertable[order] do
			order = order + 1;
		end
	end
	
	return order;
end

function addWeaponDB(charnode, sourcenode, itemnode, nodetype)
	-- Parameter validation
	if not charnode or not sourcenode or not itemnode then
		return nil;
	end
	
	-- Get the weapon list we are going to add to
	local weaponlist = NodeManager.createChild(charnode, "weaponlist");
	if not weaponlist then
		return nil;
	end
	
	-- Grab some information from the sourcenode to populate the new weapon entries
	local type = NodeManager.get(sourcenode, "type", "");
	local range = NodeManager.get(sourcenode, "range", 0);
	
	local name = NodeManager.get(sourcenode, "name", "");
	local properties = NodeManager.get(sourcenode, "properties", "");
	
	local critical = NodeManager.get(sourcenode, "critical", "");
	local criticaldice = {};
	local criticalbonus = 0;
	local criticaldmgtype = "";
	if critical ~= "" then
		local starts, ends, dicestr, typestr = string.find(critical, "%+(%d[d%+%d%s]*)%s?(%a*)%scritical%sdamage");
		if starts then
			critical = string.sub(critical, ends+1);
			if string.sub(critical, 1, 5) == ", or " then
				critical = string.sub(critical, 6);
			elseif string.sub(critical, 1, 6) == ", and " then
				critical = string.sub(critical, 7);
			end
			
			criticaldice, criticalbonus = StringManager.convertStringToDice(dicestr);
			criticaldmgtype = typestr;
		end
		if critical ~= "" then
			if properties ~= "" then
				properties = properties .. ", ";
			end
			properties = properties .. "Crit: " .. critical;
		end
	end

	local profbonus = NodeManager.get(sourcenode, "profbonus", 0);
	local enhancebonus = NodeManager.get(sourcenode, "bonus", 0);

	local damagestring = NodeManager.get(sourcenode, "damage", "");
	local dice, modifier = StringManager.convertStringToDice(damagestring);
	
	local weaponnode = NodeManager.createChild(weaponlist);
	local weaponnode2 = nil;
	if weaponnode then
		-- Set the shortcut fields
		local refnode = NodeManager.createChild(weaponnode, "shortcut", "windowreference");
		refnode.setValue(nodetype, itemnode.getNodeName());

		-- Calculate the order number
		local order = calcNextWeaponOrder(weaponlist);
		NodeManager.set(weaponnode, "order", "number", order);
		
		-- Fill in the basic, attack and damage fields
		NodeManager.set(weaponnode, "name", "string", name);
		NodeManager.set(weaponnode, "properties", "string", properties);
		NodeManager.set(weaponnode, "bonus", "number", profbonus + enhancebonus);
		NodeManager.set(weaponnode, "damagedice", "dice", dice);
		NodeManager.set(weaponnode, "damagebonus", "number", modifier + enhancebonus);
		NodeManager.set(weaponnode, "criticaldice", "dice", criticaldice);
		NodeManager.set(weaponnode, "criticalbonus", "number", criticalbonus);
		NodeManager.set(weaponnode, "criticaldamagetype", "string", criticaldmgtype);

		if type == "Melee" then
			-- Fill in the melee specific fields
			NodeManager.set(weaponnode, "type", "number", 0);
			NodeManager.set(weaponnode, "attackdef", "string", "ac");

			if string.match(string.lower(properties), "heavy thrown") or string.match(string.lower(properties), "light thrown") then
				weaponnode2 = NodeManager.createChild(weaponlist);
				if weaponnode2 then
					-- Set the shortcut fields
					local refnode = NodeManager.createChild(weaponnode2, "shortcut", "windowreference");
					refnode.setValue(nodetype, itemnode.getNodeName());

					-- Calculate the order number
					local order = calcNextWeaponOrder(weaponlist);
					NodeManager.set(weaponnode2, "order", "number", order);

					-- Fill in the basic, attack and damage fields
					NodeManager.set(weaponnode2, "type", "number", 1);
					NodeManager.set(weaponnode2, "name", "string", name);
					NodeManager.set(weaponnode2, "properties", "string", properties);

					NodeManager.set(weaponnode2, "rangeincrement", "number", range);
					NodeManager.set(weaponnode2, "bonus", "number", profbonus + enhancebonus);
					NodeManager.set(weaponnode2, "attackdef", "string", "ac");

					NodeManager.set(weaponnode2, "damagedice", "dice", dice);
					NodeManager.set(weaponnode2, "damagebonus", "number", modifier + enhancebonus);
					NodeManager.set(weaponnode2, "criticaldice", "dice", criticaldice);
					NodeManager.set(weaponnode2, "criticalbonus", "number", criticalbonus);
					NodeManager.set(weaponnode2, "criticaldamagetype", "string", criticaldmgtype);
				end
			end

		elseif type == "Ranged" then
			-- Fill in the ranged specific fields
			NodeManager.set(weaponnode, "type", "number", 1);
			NodeManager.set(weaponnode, "rangeincrement", "number", range);
			NodeManager.set(weaponnode, "attackdef", "string", "ac");

		elseif type == "Implement" then
			-- Fill in the implement specific fields
			NodeManager.set(weaponnode, "type", "number", 1);

		else
			-- Fill in the general magic weapon fields
			NodeManager.set(weaponnode, "type", "number", 0);
			NodeManager.set(weaponnode, "attackdef", "string", "ac");
		end
	end
	
	return weaponnode, weaponnode2;
end

function getPowerFocus(powerattacknode)
	-- Validate parameters
	if not powerattacknode then
		return nil;
	end
	
	-- Get this attack's focus, if any
	local tool_order = NodeManager.get(powerattacknode, "focus", 0);
	
	-- If no focus specified, then look up the default for this type of power
	if tool_order == 0 then
		local isWeaponPower = false;
		local isImplementPower = false;

		local keywords = string.lower(NodeManager.get(powerattacknode, "...keywords", ""));
		if string.match(keywords, "weapon") then
			isWeaponPower = true;
		elseif string.match(keywords, "implement") then
			isImplementPower = true;
		end

		if isWeaponPower then
			tool_order = NodeManager.get(powerattacknode, ".......powerfocus.weapon.order", 0);
		elseif isImplementPower then
			tool_order = NodeManager.get(powerattacknode, ".......powerfocus.implement.order", 0);
		end
	end
	
	-- Look up the weapon or implement specified
	local weapon_node = nil;
	if tool_order > 0 then
		local weapons_node = powerattacknode.getChild(".......weaponlist");
		if weapons_node then
			for k, v in pairs(weapons_node.getChildren()) do
				local order = NodeManager.get(v, "order", 0);
				if order == tool_order then
					weapon_node = v;
				end
			end
		end
	end
	
	-- Return the node of the weapon used as a focus for this power
	return weapon_node;
end

function getPowerAbilityOutputOrder(powerabilitynode)
	-- Default value
	local outputorder = 1;
	
	-- Validate parameters
	if not powerabilitynode then
		return outputorder;
	end
	local abilitylistnode = powerabilitynode.getParent();
	if not abilitylistnode then
		return outputorder;
	end
	
	-- First, pull some ability attributes
	local abilitytype = NodeManager.get(powerabilitynode, "type", "");
	local abilityorder = NodeManager.get(powerabilitynode, "order", 0);
	
	-- Iterate through list node
	for k,v in pairs(abilitylistnode.getChildren()) do
		if NodeManager.get(v, "type", "") == abilitytype then
			if NodeManager.get(v, "order", 0) < abilityorder then
				outputorder = outputorder + 1;
			end
		end
	end
	
	return outputorder;
end

function useHealingSurge(nodeChar)
	-- Get the character's current wounds value
	local woundsval = NodeManager.get(nodeChar, "hp.wounds", 0);
	
	-- If the character is not wounded, then let the user know and exit
	if woundsval <= 0 then
		ChatManager.Message("Character is unwounded, healing surge not used.", false, CombatCommon.getActor("pc", nodeChar));
		return;
	end
	
	-- Determine whether the character has any healing surges remaining
	local expendval = NodeManager.get(nodeChar, "hp.surgesused", 0);
	local maxval = NodeManager.get(nodeChar, "hp.surgesmax", 0);
	if expendval >= maxval then
		ChatManager.Message("Character has no healing surges remaining.", false, CombatCommon.getActor("pc", nodeChar));
		return;
	end
	
	-- Determine the message and amount of healing surge
	local text = "Healing surge used.";
	local surgeval = NodeManager.get(nodeChar, "hp.surge", 0);
	if not ModifierStack.isEmpty() then
		text = text .. " (" .. ModifierStack.getDescription(true) .. ")";
		surgeval = surgeval + ModifierStack.getSum();
		ModifierStack.reset();
	end
	
	-- Determine if wounds are greater than hit points.
	-- Applying a healing surge returns the character to zero, before applying healing surge
	local hpval = NodeManager.get(nodeChar, "hp.total", 0);
	if woundsval > hpval then
		woundsval = hpval;
	end
	
	-- Apply the healing surge
	NodeManager.set(nodeChar, "hp.surgesused", "number", expendval + 1);
	NodeManager.set(nodeChar, "hp.wounds", "number", math.max(woundsval - surgeval, 0));
	
	-- Send the message to everyone
	ChatManager.Message(text, true, CombatCommon.getActor("pc", nodeChar));
end

function useActionPoint(nodeChar)
	
	-- CHECK TO SEE IF ACTION POINT ALREADY USED
	if NodeManager.get(nodeChar, "apused", 0) == 1 then
		ChatManager.Message("Action point already used since last rest.", false, CombatCommon.getActor("pc", nodeChar));
		return;
	end
	
	-- CHECK FOR LACK OF ACTION POINTS
	local nActionPoints = NodeManager.get(nodeChar, "actionpoints", 0);
	if nActionPoints <= 0 then
		ChatManager.Message("No action points remaining.", false, CombatCommon.getActor("pc", nodeChar));
		return;
	end

	-- USE ACTION POINT
	NodeManager.set(nodeChar, "actionpoints", "number", nActionPoints - 1);
	NodeManager.set(nodeChar, "apused", "number", 1);
	ChatManager.Message("Action point used.", true, CombatCommon.getActor("pc", nodeChar));
end

function rest(nodeChar, extended, milestone)
	
	-- RESET POWERS
	resetPowers(nodeChar, extended, milestone);
	
	-- RESET HEALTH
	resetHealth(nodeChar, extended);
	
	-- RESET ACTION POINTS
	if extended then
		NodeManager.set(nodeChar, "actionpoints", "number", 1);
	elseif milestone then
		local nActionPoints = NodeManager.get(nodeChar, "actionpoints", 0);
		NodeManager.set(nodeChar, "actionpoints", "number", nActionPoints + 1);
	end
	NodeManager.set(nodeChar, "apused", "number", 0);
end

function resetPowers(charnode, extended, milestone)
	-- Get the overall powernode
	local powersnode = charnode.getChild("powers");
	if not powersnode then
		return;
	end
	
	-- Cycle over each power type
	-- If extended rest, then reset all powers
	-- If short rest, then reset encounter powers only
	for typekey,typenode in pairs(powersnode.getChildren()) do
		local powerhighnode = typenode.getChild("power");
		if powerhighnode then
			-- Reset the individual powers
			for powerkey,powernode in pairs(powerhighnode.getChildren()) do
				if extended == true then
					NodeManager.set(powernode, "used", "number", 0);
				else
					local rechargeval = string.lower(string.sub(NodeManager.get(powernode, "recharge", ""),1,3));
					if rechargeval == "enc" then
						NodeManager.set(powernode, "used", "number", 0);
					end
				end
			end
			
			-- Reset the daily item power limit
			if typenode.getName() == "e-itemdaily" then
				if NodeManager.get(typenode, "usesavailable", 0) ~= 0 then
					local maxuses = math.ceil(NodeManager.get(charnode, "level", 0) / 10);
					if maxuses < 0 then
						maxuses = 1;
					end
					
					if extended then
						NodeManager.set(typenode, "usesavailable", "number", maxuses);
					elseif milestone then
						local currentuses = NodeManager.get(typenode, "usesavailable", 0);
						NodeManager.set(typenode, "usesavailable", "number", currentuses + 1);
					end
				end
			end
		end
	end
end

function resetHealth(charnode, extended)
	NodeManager.set(charnode, "hp.temporary", "number", 0);
	NodeManager.set(charnode, "hp.secondwind", "number", 0);
	NodeManager.set(charnode, "hp.faileddeathsaves", "number", 0);
	
	if extended == true then
		NodeManager.set(charnode, "hp.wounds", "number", 0);
		NodeManager.set(charnode, "hp.surgesused", "number", 0);
	end
end

function onDragPowerAbility(powerabilitynode, draginfo, subtype)
	local ability_type = NodeManager.get(powerabilitynode, "type", "");
	if ability_type == "attack" then
		if subtype == "attack" then
			local rActor, rAbility, rFocus = getAdvancedRollStructures("attack", powerabilitynode, getPowerFocus(powerabilitynode));
			local attack_name, attack_dice, attack_mod = RulesManager.buildAttackRoll(rActor, rAbility, rFocus);
			return RulesManager.dragAction(draginfo, "attack", attack_mod, attack_name, rActor, attack_dice, true);

		elseif subtype == "damage" then
			local rActor, rAbility, rFocus = getAdvancedRollStructures("damage", powerabilitynode, getPowerFocus(powerabilitynode));
			local damage_name, damage_dice, damage_mod = RulesManager.buildDamageRoll(rActor, rAbility, rFocus);
			return RulesManager.dragAction(draginfo, "damage", damage_mod, damage_name, rActor, damage_dice, true);
		end
		
	elseif ability_type == "heal" then
		local rActor, rAbility, rFocus = getAdvancedRollStructures("heal", powerabilitynode, nil);
		local heal_name, heal_dice, heal_mod = RulesManager.buildHealRoll(rActor, rAbility, rFocus)
		return RulesManager.dragAction(draginfo, "damage", heal_mod, heal_name, rActor, heal_dice, true);
	
	elseif ability_type == "effect" then
		local rActor, rEffect = getEffectStructures(powerabilitynode);
		return RulesManager.dragEffect(draginfo, rActor, rEffect);
	end
	
	return false;
end

function onDoubleClickPowerAbility(powerabilitynode, subtype)
	local ability_type = NodeManager.get(powerabilitynode, "type", "");
	if ability_type == "attack" then
		if subtype == "attack" then
			local rActor, rAbility, rFocus = getAdvancedRollStructures("attack", powerabilitynode, getPowerFocus(powerabilitynode));
			local attack_name, attack_dice, attack_mod = RulesManager.buildAttackRoll(rActor, rAbility, rFocus);
			return RulesManager.dclkAction("attack", attack_mod, attack_name, rActor, nil, attack_dice);

		elseif subtype == "damage" then
			local rActor, rAbility, rFocus = getAdvancedRollStructures("damage", powerabilitynode, getPowerFocus(powerabilitynode));
			local damage_name, damage_dice, damage_mod = RulesManager.buildDamageRoll(rActor, rAbility, rFocus);
			return RulesManager.dclkAction("damage", damage_mod, damage_name, rActor, nil, damage_dice);
		end
		
	elseif ability_type == "heal" then
		local rActor, rAbility, rFocus = getAdvancedRollStructures("heal", powerabilitynode, nil);
		local heal_name, heal_dice, heal_mod = RulesManager.buildHealRoll(rActor, rAbility, rFocus)
		return RulesManager.dclkAction("damage", heal_mod, heal_name, rActor, nil, heal_dice);

	elseif ability_type == "effect" then
		local rActor, rEffect = getEffectStructures(powerabilitynode);
		return RulesManager.dclkEffect(rActor, rEffect);
	end

	return false;
end

function onDropPowerAbility(powerabilitylist, draginfo)
	local rEffect = RulesManager.decodeEffectFromDrag(draginfo);
	if rEffect then
		local newability = powerabilitylist.createChild();
		if newability then
			NodeManager.set(newability, "type", "string", "effect");
			NodeManager.set(newability, "label", "string", rEffect.sName);
			NodeManager.set(newability, "savemod", "number", rEffect.nSaveMod);
			NodeManager.set(newability, "expiration", "string", rEffect.sExpire);
			NodeManager.set(newability, "apply", "string", rEffect.sApply);
		end

		return true;
	end
	
	return false;
end

function getEffectStructures(nodeAbility)
	-- ACTOR
	local nodeChar = nil;
	if nodeAbility then
		nodeChar = nodeAbility.getChild(".......");
	end
	local rActor = CombatCommon.getActor("pc", nodeChar);

	local rEffect = {};

	rEffect.sName = EffectsManager.evalEffect(rActor, NodeManager.get(nodeAbility, "label", ""));
	rEffect.sExpire = NodeManager.get(nodeAbility, "expiration", "");
	rEffect.nSaveMod = NodeManager.get(nodeAbility, "savemod", 0);
	rEffect.sApply = NodeManager.get(nodeAbility, "apply", "");
	rEffect.sTargeting = NodeManager.get(nodeAbility, "targeting", "");
	
	if rActor then
		rEffect.sSource = rActor.sCTNode;
		rEffect.nInit = NodeManager.get(rActor.nodeCT, "initresult", 0);
	else
		rEffect.sSource = "";
		rEffect.nInit = 0;
	end
	
	return rActor, rEffect;
end

function getBaseAttackRollStructures(attack_name, char_node)
	-- CREATURE
	local rCreature = CombatCommon.getActor("pc", char_node);

	-- ABILITY
	local rAbility = {};
	rAbility.type = "attack";
	rAbility.name = attack_name;
	if string.match(string.lower(attack_name), "melee") then
		rAbility.range = "M";
	else
		rAbility.range = "R";
	end
	rAbility.defense = "ac";
	
	local rAbilityClause = {};
	rAbilityClause.stat = {};
	rAbilityClause.mod = 0;
	if rAbility.range == "R" then
		local statval = NodeManager.get(rCreature.nodeCreature, "attacks.ranged.abilityname", "");
		if statval == "" then
			statval = "dexterity";
		end
		table.insert(rAbilityClause.stat, statval);
	elseif rAbility.range == "M" then
		local statval = NodeManager.get(rCreature.nodeCreature, "attacks.melee.abilityname", "");
		if statval == "" then
			statval = "strength";
		end
		table.insert(rAbilityClause.stat, statval);
	end
	rAbility.clauses = { rAbilityClause };
		
	-- RESULTS
	return rCreature, rAbility, nil;
end

function getAdvancedRollStructures(ability_type, ability_node, focus_node)
	-- CREATURE
	local char_node = nil;
	if ability_node then
		char_node = ability_node.getChild(".......");
	elseif focus_node then
		char_node = focus_node.getChild("...");
	end
	local rCreature = CombatCommon.getActor("pc", char_node);
	
	-- ABILITY
	local rAbility = nil;
	if ability_node then
		rAbility = {};
		rAbility.type = ability_type;
		rAbility.name = NodeManager.get(ability_node, "...name", "");
		rAbility.order = getPowerAbilityOutputOrder(ability_node);
		
		local listRangeWords = StringManager.parseWords(string.lower(NodeManager.get(ability_node, "...range", "")));
		local bMelee = StringManager.isWord("melee", listRangeWords);
		local bRanged = StringManager.isWord("ranged", listRangeWords);
		if bMelee and bRanged then
			rAbility.range = "*";
		elseif bMelee then
			rAbility.range = "M";
		elseif bRanged then
			rAbility.range = "R";
		elseif StringManager.isWord(listRangeWords[1], "close") then
			rAbility.range = "C";
		elseif StringManager.isWord(listRangeWords[1], "area") then
			rAbility.range = "A";
		else
			rAbility.range = "";
		end
		rAbility.clauses = {};

		local rAbilityClause = {}; 

		if ability_type == "attack" then
			rAbility.defense = NodeManager.get(ability_node, "attackdef", 0);
			rAbilityClause.stat = { NodeManager.get(ability_node, "attackstat", "") };
			rAbilityClause.mod = NodeManager.get(ability_node, "attackstatmodifier", 0);

		elseif ability_type == "damage" then

			rAbilityClause.basemult = NodeManager.get(ability_node, "damageweaponmult", 0);
			rAbilityClause.subtype = NodeManager.get(ability_node, "damagetype", "");

			local powerdice = NodeManager.get(ability_node, "damagebasicdice", {});
			local powermod = NodeManager.get(ability_node, "damagestatmodifier", 0);
			rAbilityClause.dicestr = StringManager.convertDiceToString(powerdice, powermod);
			rAbilityClause.critdicestr = "";

			rAbilityClause.stat = {};
			local statval = NodeManager.get(ability_node, "damagestat", "");
			if statval ~= "" then
				local statmult = NodeManager.get(ability_node, "damagestatmult", "");
				if statmult == "half" then
					statval = "half" .. statval;
				elseif statmult == "double" then
					statval = "double" .. statval;
				end

				table.insert(rAbilityClause.stat, statval);
			end
			statval = NodeManager.get(ability_node, "damagestat2", "");
			if statval ~= "" then
				local statmult = NodeManager.get(ability_node, "damagestatmult2", "");
				if statmult == "half" then
					statval = "half" .. statval;
				elseif statmult == "double" then
					statval = "double" .. statval;
				end

				table.insert(rAbilityClause.stat, statval);
			end

		elseif ability_type == "heal" then

			rAbilityClause.basemult = NodeManager.get(ability_node, "hsvmult", 0);
			rAbilityClause.subtype = NodeManager.get(ability_node, "healtype", "");
			rAbilityClause.cost = NodeManager.get(ability_node, "healcost", 0);

			local powerdice = NodeManager.get(ability_node, "healdice", {});
			local powermod = NodeManager.get(ability_node, "healmod", 0);
			rAbilityClause.dicestr = StringManager.convertDiceToString(powerdice, powermod);

			rAbilityClause.stat = {};
			local statval = NodeManager.get(ability_node, "healstat", "");
			if statval ~= "" then
				local statmult = NodeManager.get(ability_node, "healstatmult", "");
				if statmult == "half" then
					statval = "half" .. statval;
				elseif statmult == "double" then
					statval = "double" .. statval;
				end

				table.insert(rAbilityClause.stat, statval);
			end
			statval = NodeManager.get(ability_node, "healstat2", "");
			if statval ~= "" then
				local statmult = NodeManager.get(ability_node, "healstatmult2", "");
				if statmult == "half" then
					statval = "half" .. statval;
				elseif statmult == "double" then
					statval = "double" .. statval;
				end

				table.insert(rAbilityClause.stat, statval);
			end
		end
		
		table.insert(rAbility.clauses, rAbilityClause);
	end
	
	-- FOCUS
	local rFocus = getFocusRecord(ability_type, focus_node);
	
	-- FIX POWERS THAT HAVE DIFFERENT RANGES BASED ON FOCUS
	if rAbility and rAbility.range == "*" then
		if rFocus then
			rAbility.range = rFocus.range;
		end
	end
	
	-- RESULTS
	return rCreature, rAbility, rFocus;
end

function getFocusRecord(ability_type, focus_node)
	-- VALIDATE
	if not focus_node then
		return nil;
	end
	if ability_type ~= "attack" and ability_type ~= "damage" then
		return nil;
	end
	
	-- SETUP
	local char_node = nil;
	if focus_node then
		char_node = focus_node.getChild("...");
	end
	
	-- BUILD FOCUS RECORD
	local rFocus = {};
	rFocus.type = ability_type;
	rFocus.name = NodeManager.get(focus_node, "name", "");
	rFocus.range = "R";
	if NodeManager.get(focus_node, "type", 0) == 0 then
		rFocus.range = "M";
	end

	-- BUILD FOCUS RECORD CLAUSE BASED ON ABILITY TYPE
	local rFocusClause = {};
	if ability_type == "attack" then
		local statval = NodeManager.get(focus_node, "attackstat", "");
		if statval == "" then
			if rFocus.range == "R" then
				statval = NodeManager.get(char_node, "attacks.ranged.abilityname", "");
				if statval == "" then
					statval = "dexterity";
				end
			else
				statval = NodeManager.get(char_node, "attacks.melee.abilityname", "");
				if statval == "" then
					statval = "strength";
				end
			end
		end
		rFocus.properties = string.lower(NodeManager.get(focus_node, "properties", ""));
		rFocus.defense = NodeManager.get(focus_node, "attackdef", 0);
		rFocusClause.stat = { statval };
		rFocusClause.mod = NodeManager.get(focus_node, "bonus", 0);

	elseif ability_type == "damage" then
		rFocus.properties = string.lower(NodeManager.get(focus_node, "properties", ""));
		rFocus.basedice = NodeManager.get(focus_node, "damagedice", {});
		rFocus.critdice = NodeManager.get(focus_node, "criticaldice", {});
		rFocus.critmod = NodeManager.get(focus_node, "criticalbonus", 0);
		rFocus.crittype = NodeManager.get(focus_node, "criticaldamagetype", "");

		rFocusClause.mod = NodeManager.get(focus_node, "damagebonus", 0);
		rFocusClause.dicestr = StringManager.convertDiceToString(rFocus.basedice, rFocusClause.mod);
		rFocusClause.subtype = NodeManager.get(focus_node, "damagetype", "");

		local dmgstat = NodeManager.get(focus_node, "damagestat", "");
		if dmgstat == "" then
			if rFocus.range == "R" then
				dmgstat = "dexterity";
			else
				dmgstat = "strength";
			end
		end
		rFocusClause.stat = { dmgstat };
	end
	rFocus.clauses = { rFocusClause };
	
	-- RESULTS
	return rFocus;
end

function getWeaponCritical(weapon_node)

	-- GET CRITICAL DICE
	local dice = NodeManager.get(weapon_node, "criticaldice", {});

	-- GET CRITICAL MODIFIER
	local mod = NodeManager.get(weapon_node, "criticalbonus", 0);

	-- BUILD OUTPUT
	local s = "[DAMAGE]";
	
	-- ADD NAME
	s = s .. " " .. NodeManager.get(weapon_node, "name", "");
	
	-- ADD DAMAGE TYPE
	local critdmgtype = NodeManager.get(weapon_node, "criticaldamagetype", "");
	if critdmgtype ~= "" then
		s = s .. " [TYPE: " .. critdmgtype .. "]";
	end

	-- ADD NOTE ABOUT CRITICAL DICE ONLY
	s = s .. " [CRITICAL BONUS DICE ONLY]";
	
	-- RESULTS
	return s, dice, mod;
end

function getSkillRollStructures(nodeSkill)
	local nodeChar = nil;
	if nodeSkill then
		nodeChar = nodeSkill.getChild("...");
	end
	
	local sSkillName = NodeManager.get(nodeSkill, "label", "");
	local nSkillMod = NodeManager.get(nodeSkill, "total", 0);
	local sSkillStat = NodeManager.get(nodeSkill, "statname", "");
	
	return CombatCommon.getSkillRollStructures("pc", nodeChar, sSkillName, nSkillMod, sSkillStat);
end
